package com.phiture.erp.produce.core.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.phiture.erp.basic.api.pojo.resp.ErpMaterialResp;
import com.phiture.erp.basic.api.service.ErpMaterialApi;
import com.phiture.erp.common.constants.ErpNoPrefixConstants;
import com.phiture.erp.common.enums.ErpMaterialAttrEnum;
import com.phiture.erp.common.utils.ErpNoGenerator;
import com.phiture.erp.produce.core.event.BomInfoChangedEvent;
import com.phiture.erp.produce.core.pojo.vo.ErpBomPageReqVO;
import com.phiture.erp.produce.core.pojo.vo.ErpBomSaveReqVO;
import com.phiture.erp.produce.core.service.ErpBomService;
import com.phiture.erp.produce.dal.entity.ErpBomDO;
import com.phiture.erp.produce.dal.entity.ErpBomItemsDO;
import com.phiture.erp.produce.dal.mapper.ErpBomItemsMapper;
import com.phiture.erp.produce.dal.mapper.ErpBomMapper;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;

import javax.annotation.Resource;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static com.phiture.erp.common.constants.ErrorCodeConstants.*;

/**
 * bom Service 实现类
 *
 * @author lekaihuai
 */
@Service
@Validated
public class ErpBomServiceImpl implements ErpBomService {

    @Resource
    private ErpBomMapper bomMapper;
    @Resource
    private ErpBomItemsMapper bomItemsMapper;

    @Resource
    private ErpMaterialApi erpMaterialApi;

    @Resource
    private ErpNoGenerator erpNoGenerator;
    @Resource
    private ApplicationEventPublisher eventPublisher;


    @Override
    @Transactional(rollbackFor = Exception.class)
    public Long createBom(ErpBomSaveReqVO createReqVO) {


        // 过滤出物料属性为自制件的物料
        Set<Long> selfMadePartsMaterialIds =  createReqVO.getBomItemss().stream()
                .filter(item -> ErpMaterialAttrEnum.SELF_MADE_PARTS.getAttr().equals(item.getFmaterialprop()))
                .map(ErpBomItemsDO::getFmaterialid).collect(Collectors.toSet());
        // 对自制件物料进行校验，检查是否存在对应的BOM
        if (!checkMaterialHasBom(selfMadePartsMaterialIds)) {
            throw exception(BOM_SLEFMADE_NOT_EXISTS);
        }

        // 自动生成 BOM 编号
        String bomNo = erpNoGenerator.generate(ErpNoPrefixConstants.BOM_NO_PREFIX);
        validateBomNo(bomNo);
        createReqVO.setFnumber(bomNo);
        // 插入
        ErpBomDO bom = BeanUtils.toBean(createReqVO, ErpBomDO.class);

        // 查询在BOM表中是否该物料已经存在
        boolean exists = bomMapper.exists(
                Wrappers.lambdaQuery(ErpBomDO.class)
                      .eq(ErpBomDO::getFmaterialid, createReqVO.getFmaterialid())
                      .eq(ErpBomDO::getDeleted, Boolean.FALSE)
        );
        // 如果存在，抛出异常
        if (exists) {
            throw exception(BOM_MATERIAL_EXISTS);
        }
        bomMapper.insert(bom);

        // 插入子表
        createBomItemsList(bom.getFid(), createReqVO.getBomItemss());
        // 返回
        return bom.getFid();
    }

    private void validateBomNo(String bomNo){
        boolean exists = bomMapper.exists(
                Wrappers.lambdaQuery(ErpBomDO.class)
                        .eq(ErpBomDO::getFnumber, bomNo)
                        .eq(ErpBomDO::getDeleted, Boolean.FALSE)
        );
        if(exists){
            throw exception(BOM_NO_EXISTS);
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateBom(ErpBomSaveReqVO updateReqVO) {
        // 校验存在
        validateBomExists(updateReqVO.getFid());
        // 更新
        ErpBomDO updateObj = BeanUtils.toBean(updateReqVO, ErpBomDO.class);
        bomMapper.updateById(updateObj);

        // 更新子表
        updateBomItemsList(updateReqVO.getFid(), updateReqVO.getBomItemss());
        // 更新BOM同时更新缓存
        bomChangedPublishEvent(updateReqVO.getFmaterialid());
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deleteBom(Long id) {
        // 校验存在
        Long materialId = validateBomExists(id);
        // 删除
        bomMapper.deleteById(id);

        // 删除子表
        deleteBomItemsByFparentid(id);

        // 更新BOM同时更新缓存
        bomChangedPublishEvent(materialId);
    }

    private Long validateBomExists(Long id) {
        ErpBomDO erpBomDO = bomMapper.selectById(id);
        if (erpBomDO == null) {
            throw exception(BOM_NOT_EXISTS);
        }
        return erpBomDO.getFmaterialid();
    }

    @Override
    public ErpBomDO getBom(Long id) {
        return bomMapper.selectById(id);
    }

    @Override
    public ErpBomDO getBomByMaterialId(Long materialId) {
        return bomMapper.selectOne(Wrappers.lambdaQuery(ErpBomDO.class)
              .eq(ErpBomDO::getFmaterialid, materialId)
              .eq(ErpBomDO::getDeleted, Boolean.FALSE));
    }


    @Override
    public PageResult<ErpBomDO> getBomPage(ErpBomPageReqVO pageReqVO) {
        PageResult<ErpBomDO> result = bomMapper.selectPage(pageReqVO, new LambdaQueryWrapperX<ErpBomDO>()
                .eqIfPresent(ErpBomDO::getFnumber, pageReqVO.getFnumber())
                .likeIfPresent(ErpBomDO::getFname, pageReqVO.getFname())
                .eqIfPresent(ErpBomDO::getFmaterialid, pageReqVO.getFmaterialid())
                .eqIfPresent(ErpBomDO::getFbasestatus, pageReqVO.getFbasestatus())
                .orderByDesc(ErpBomDO::getFid));
        return result;
    }

    @Override
    public List<ErpMaterialResp> getUnBomMaterial() {
        // 调用物料API获取物料列表
        List<ErpMaterialResp> list = erpMaterialApi.getMaterialSelfMadeSimpleList();
//        // 在BOM表中查询出所有有效的BOM
//        List<ErpBomDO> bomList = bomMapper.selectList(Wrappers.lambdaQuery(ErpBomDO.class)
//                .eq(ErpBomDO::getDeleted, Boolean.FALSE));
//
//        if (bomList != null && !bomList.isEmpty()) {
//            Set<Long> bomMaterialIds = bomList.stream()
//                    .map(ErpBomDO::getFmaterialid)
//                    .collect(Collectors.toSet());
//            list.removeIf(material -> bomMaterialIds.contains(material.getId()));
//        }
        return list;
    }

    // ==================== 子表（bom分录） ====================

    @Override
    public List<ErpBomItemsDO> getBomItemsListByFparentid(Long fparentid) {
        return bomItemsMapper.selectListByFparentid(fparentid);
    }

    @Override
    public List<Long> getMaterialIdFromBom(Collection<Long> materialIds) {
        List<ErpBomDO> list = bomMapper.selectList(
                Wrappers.lambdaQuery(ErpBomDO.class)
                        .select(ErpBomDO::getFmaterialid)
                        .in(ErpBomDO::getFmaterialid, materialIds)
        );
        return CollUtil.isEmpty(list) ? Collections.emptyList() : list.stream().map(ErpBomDO::getFmaterialid).collect(Collectors.toList());
    }

    @Override
    public Boolean checkMaterialHasBom(Collection<Long> materialIds) {
            if(CollUtil.isEmpty(materialIds)){
                return true;
            }
            List<Long> bomMaterialIds = this.getMaterialIdFromBom(materialIds);
            if (CollUtil.isEmpty(bomMaterialIds) || materialIds.size() < bomMaterialIds.size()) {
                return false;
            }
            return true;
    }

    private void createBomItemsList(Long fparentid, List<ErpBomItemsDO> list) {
        // 使用 IntStream 来遍历列表，设置 fseq 为循环变量的 index
        IntStream.range(0, list.size()).forEach(index -> {
            ErpBomItemsDO item = list.get(index);
            item.setFparentid(fparentid);
            item.setFseq((long)index); // 设置 fseq 为当前索引
        });
        bomItemsMapper.insertBatch(list);
    }

    private void updateBomItemsList(Long fparentid, List<ErpBomItemsDO> list) {
        deleteBomItemsByFparentid(fparentid);
        IntStream.range(0, list.size()).forEach(index -> {
            ErpBomItemsDO item = list.get(index);
            item.setFparentid(fparentid);
            item.setFseq((long)index); // 设置 fseq 为当前索引
            item.setFid(null);
            item.setUpdater(null);
            item.setUpdateTime(null);
        });
//		list.forEach(o -> o.setFid(null).setUpdater(null).setUpdateTime(null)); // 解决更新情况下：1）id 冲突；2）updateTime 不更新
        createBomItemsList(fparentid, list);
    }

    private void deleteBomItemsByFparentid(Long fparentid) {
        bomItemsMapper.deleteByFparentid(fparentid);
    }

    /**
     * BOM发生变更，发送事件
     */
    private void bomChangedPublishEvent(Long materialId){
        eventPublisher.publishEvent(new BomInfoChangedEvent(this, materialId));
    }

}